#include "ioqueue.h"
#include "debug.h"
#include "interrupt.h"
#include "global.h"
#include "sync.h"

void ioqueue_init(struct ioqueue* ioq)
{
   lock_init(&ioq->queue_lock); 
   ioq->consumer = ioq->producter = NULL;
   ioq->head = ioq->tail = 0;
}

static int32_t next_pos(int32_t pos)
{
    return (pos+1) % bufsize;
}

bool ioq_full(struct ioqueue* ioq)
{
    ASSERT(intr_get_status() == INTR_OFF);
    return next_pos(ioq->head) == ioq->tail;
}

bool ioq_empty(struct ioqueue* ioq)
{
    ASSERT(intr_get_status() == INTR_OFF);
    return ioq->head == ioq->tail;
}

static void ioq_wait(struct task_struct** waiter)
{
    ASSERT(*waiter == NULL && waiter != NULL);
    *waiter = running_thread();
    thread_block(TASK_BLOCKED);
}

static void ioq_wakeup(struct task_struct** waiter)
{
    ASSERT(*waiter != NULL);
    thread_unblock(*waiter);
    *waiter = NULL;
}

// 消费者从ioq中读取一字符
char ioq_getchar(struct ioqueue* ioq)
{
    ASSERT(intr_get_status() == INTR_OFF);

    while(ioq_empty(ioq)) {
        lock_acquire(&ioq->queue_lock);
        ioq_wait(&ioq->consumer);
        lock_release(&ioq->queue_lock);
    }
    
    char ch = ioq->buf[ioq->tail];
    ioq->tail = next_pos(ioq->tail);
    
    if(ioq->producter != NULL) {
        ioq_wakeup(&ioq->producter);
    }

    return ch;
}

void ioq_putchar(struct ioqueue* ioq, char ch)
{
    ASSERT(intr_get_status() == INTR_OFF);

    while(ioq_full(ioq)) {
        lock_acquire(&ioq->queue_lock);
        ioq_wait(&ioq->producter);
        lock_release(&ioq->queue_lock);
    }

    ioq->buf[ioq->head] = ch;
    ioq->head = next_pos(ioq->head);

    if(ioq->consumer != NULL) {
        ioq_wakeup(&ioq->consumer);
    }
}